﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;

using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using $prjsn$.Common;
using $prjsn$.Entity;
using ProjectBase.Core;

namespace $safeprojectname$
{
    public static class BusinessExt
    {
        #region Bind inner CodeListFilters via IFilterBinder
        public static void BindAllCodeListFilters<TFilter>(this TFilter filter, IValueContainer valueContainer, string prefix = null)
            where TFilter : IFilter<int>
        {
            if (filter.IsNull())
            {
                return;
            }
            var innerFilters = filter
                .GetType()
                .GetProperties()
                .Where(pt => pt.PropertyType.HasType(typeof(CodeListFilter)));

            foreach (var propertyFilter in innerFilters)
            {
                var binder = GetFilterBinder(propertyFilter.PropertyType);

                // get the prefix
                // filter name "NationalityFilter", entity name = "Nationality" (removeFilter)
                var entityName = propertyFilter.Name.Replace("Filter", string.Empty);
                var entityPrefix = prefix.Extend(entityName);

                binder.BindCodeListFilter(propertyFilter.GetValue(filter, null) as CodeListFilter, valueContainer, entityPrefix);
            }
        }

        static ICodeListFilterBinder<CodeListFilter> GetFilterBinder(Type filterType)
        {
            // the covariant type of the filtered facade
            var type = typeof(ICodeListFilterBinder<>).MakeGenericType(filterType);

            // the instance of this covariant type
            var binderInstance = ProjectBase.Core.Service.Factory.CreateInstance(type);

            // the typed instance
            var binder = binderInstance as ICodeListFilterBinder<CodeListFilter>;
            if (binder.IsNull())
            {
                return null;
            }
            return binder;
        }
        #endregion Bind inner CodeListFilters via IFilterBinder

        #region Init inner CodeListFilters via Constructor new()
        public static void InitAllCodeListFilters<TFilter>(this TFilter filter)
            where TFilter : IFilter<int>
        {
            if (filter.IsNull())
            {
                return;
            }
            var innerFilters = filter
                .GetType()
                .GetProperties()
                .Where(pt => pt.PropertyType.HasType(typeof(CodeListFilter)));

            foreach (var propertyFilter in innerFilters)
            {
                var codeListFilter = Activator.CreateInstance(propertyFilter.PropertyType);
                propertyFilter.SetValue(filter, codeListFilter, null);
            }
        }
        #endregion Init inner CodeListFilters via Constructor new()

        #region Init inner CodeListFilters via IFilterFactory
        [Obsolete("This is not correct way, how to construct inner filters")]
        public static void BuildAllCodeListFilters<TFilter>(this TFilter filter)
            where TFilter : IFilter<int>
        {
            if (filter.IsNull())
            {
                return;
            }
            var innerFilters = filter
                .GetType()
                .GetProperties()
                .Where(pt => pt.PropertyType.HasType(typeof(CodeListFilter)));

            foreach (var propertyFilter in innerFilters)
            {
                var factory = GetFilterFactory(propertyFilter.PropertyType);
                propertyFilter.SetValue(filter, factory.CreateNewFilter(), null);
            }
        }

        static IFilterFactory<IFilter<int>, int> GetFilterFactory(Type filterType)
        {
            // the covariant type of the filtered facade
            var type = typeof(IFilterFactory<,>).MakeGenericType(filterType, typeof(int));

            // the instance of this covariant type
            var factoryInstance = ProjectBase.Core.Service.Factory.CreateInstance(type);

            // the typed instance
            var factory = factoryInstance as IFilterFactory<IFilter<int>, int>;
            if (factory.IsNull())
            {
                return null;
            }
            return factory;
        }
        #endregion Init inner CodeListFilters via IFilterFactory

        #region OrderBy
        public static void ApplyOrderBy<TID>(this IFilter<TID> filter)
        {
            if (filter.IsNull() || filter.OrderRows.IsEmpty())
            {
                return;
            }
            // FirstName,  LastName   desc
            var array = filter
                .OrderRows
                .CleanString()
                .Split(',');

            // just new order
            filter.OrderBy.Clear();

            foreach (var item in array)
            {
                // LastName  desc
                var pair = item.Trim().Split(' ');
                var property = pair[0].Trim();

                if (property.IsEmpty())
                {
                    continue;
                }

                if (pair.Length == 1)
                {
                    filter.OrderBy[property] = true;
                }
                else
                {
                    filter.OrderBy[property] = pair[1] != "desc";
                }
            }
        }

        public static string CleanString(this string textWithMultiSpaces)
        {
            if (textWithMultiSpaces.IsEmpty())
            {
                return textWithMultiSpaces;
            }

            var regex = new Regex(@"[ ]{2,}", RegexOptions.None);

            return regex.Replace(textWithMultiSpaces, @" ");
        }
        #endregion OrderBy

        #region struct
        static readonly DateTime MinDate = new DateTime(1900, 1, 1);
        static readonly DateTime MaxDate = new DateTime(2999, 1, 1);

        static readonly decimal MinDecimal = new decimal(int.MinValue);
        static readonly decimal MaxDecimal = new decimal(int.MaxValue);

        public static TValueType MinValue<TValueType>(this TValueType value)
            where TValueType : struct, IComparable<TValueType>
        {
            var type = typeof(TValueType);

            if (type.IsEquivalentTo(typeof(int)))
            {
                return (TValueType)(object)int.MinValue;
            }
            if (type.IsEquivalentTo(typeof(decimal)))
            {
                return (TValueType)(object) MinDecimal;
            }
            if (type.IsEquivalentTo(typeof(DateTime)))
            {
                return (TValueType) (object) MinDate;
            }
            if (type.IsEquivalentTo(typeof(bool)))
            {
                return (TValueType)(object)false;
            }

            throw new NotSupportedException("ValueType {0} is not supported by MinValue ".FormatWith(type.Name));
        }

        public static TValueType MaxValue<TValueType>(this TValueType value)
            where TValueType : struct, IComparable<TValueType>
        {
            var type = typeof(TValueType);

            if (type.IsEquivalentTo(typeof(int)))
            {
                return (TValueType)(object)int.MaxValue;
            }
            if (type.IsEquivalentTo(typeof(decimal)))
            {
                return (TValueType)(object) MaxDecimal;
            }
            if (type.IsEquivalentTo(typeof(DateTime)))
            {
                return (TValueType)(object) MaxDate;
            }
            if (type.IsEquivalentTo(typeof(bool)))
            {
                return (TValueType)(object)true;
            }

            throw new NotSupportedException("ValueType {0} is not supported by MaxValue ".FormatWith(type.Name));
        }
        #endregion struct
    }
}
